import lvgl as lv
import math
import time

PEN_STA_HIDE = 0
PEN_STA_SHOW = 1
SCR_WIDTH = 128
SCR_HEIGHT = 128

ANGLE_UNIT_DGREE = 1
ANGLE_UNIT_RADIANS = 2

# color table
COLOR_TABLE = {"red":(255,0,0), "green":(0,255,0), "blue":(0,0,255),\
               "yellow":(255,255,0), "purple":(255,0,255), "cyan":(0,255,255),\
               "white":(255,255,255), "orange":(255,50,0), "black":(0,0,0), "gray":(0,0,0), \
               "r":(255,0,0), "g":(0,255,0), "b":(0,0,255),\
               "y":(255,255,0), "p":(255,0,255), "c":(0,255,255),\
               "w":(255,255,255), "o":(255,50,0), "k":(0,0,0) \
               }

def rgb_limit(r, g = 0, b = 0):
    if isinstance(r, str):
        if r in COLOR_TABLE:
            v_r = COLOR_TABLE[r][0]
            v_g = COLOR_TABLE[r][1]
            v_b = COLOR_TABLE[r][2]
            return v_r, v_g, v_b
        else:
            return 0, 0, 0
    elif isinstance(r, tuple):
        v_r = r[0]
        v_g = r[1]
        v_b = r[2]
    else:
        v_r = r
        v_g = g
        v_b = b        

    v_r = 0 if v_r < 0 else v_r
    v_g = 0 if v_g < 0 else v_g
    v_b = 0 if v_b < 0 else v_b
    v_r = 255 if v_r > 255 else v_r
    v_g = 255 if v_g > 255 else v_g
    v_b = 255 if v_b > 255 else v_b
    
    return v_r, v_g, v_b

class my_line(lv.line):
    def __init__(self, window, point, color, pen_size):
        self.type = "line"

        super().__init__(window)
        self.line_points = [ {"x":round(point[0]), "y":round(point[1])} ]
        
        self.style_line = lv.style_t()
        lv.style_copy(self.style_line, lv.style_plain)
        self.style_line.text.color = lv.color_make(*color)
        self.style_line.text.font = lv.user_font_12_12
        self.style_line.line.color = lv.color_make(*color)
        self.style_line.line.width = pen_size
        self.set_points(self.line_points, len(self.line_points))
        self.set_style(lv.line.STYLE.MAIN, self.style_line)
        self.align(None, lv.ALIGN.IN_TOP_LEFT, 0, 0)
        
    def add_point(self, point):
        self.line_points.append({"x":point[0], 'y':point[1]})
        self.set_points(self.line_points, len(self.line_points))

    def get_point(self):
        return self.line_points

class my_label(lv.label):
    def __init__(self, window, text = ""):
        self.type = "label"
        super().__init__(window)
        self.set_text(text)

class pen():
    def __init__(self, window, point = [SCR_WIDTH // 2, SCR_HEIGHT // 2]):
        line1 = my_line(window, point, (0xff, 0, 0), 1)

        self.window = window
        self.window_style = lv.style_t()
        lv.style_copy(self.window_style, self.window.get_style())
        self.window.set_style(self.window_style)

        self.window.clean()
        self._reset()
        
        self.tracer_flag = True

        self.scale_cof = 1
        self.goto(0, 0)
        
        self.filling_status = False
        self.filling_points = []

        self.tag_list = [0]
        self.tag_id = 0 
#########################################        
        self.fd = self.forward
        self.bk = self.backward
        self.back = self.backward
        self.lt = self.left
        self.rt = self.right
        self.setpos = self.goto
        self.setposition = self.goto
        self.seth = self.setheading

        self.up = self.penup 
        self.pu = self.penup 
        self.down = self.pendown
        self.pd = self.pendown

        self.pos = self.position



    def _sync_delay(self, t = 0.05):
        if self.tracer_flag:
            time.sleep(t)

    def _reset(self, point = [SCR_WIDTH // 2, SCR_HEIGHT // 2], color = (0xff,0x00,0x00)):
        line1 = my_line(self.window, point, color, 1)
        self.line_list = [line1]
        self.current_line = line1
        self.current_position = point
        self.current_dir = 0
        self.current_color = color
        self.current_size = 1
        self.pen_status = PEN_STA_SHOW

        self.angle_unit = ANGLE_UNIT_DGREE
    
        self.last_pendown_position = point
        self.last_color = color
        self.last_size = 1

    def _pos_adapter(self, x, y):
        return [round(x * self.scale_cof + SCR_WIDTH / 2), round(-y * self.scale_cof + SCR_HEIGHT / 2)]

    def _pos_adapter_y(self, y):
        return round(-y * self.scale_cof + SCR_WIDTH / 2)

    def _pos_adapter_x(self, x):
        return round(x * self.scale_cof+ SCR_HEIGHT / 2)

    def _create_new_line(self):
        '''
        if pen_color/pen_size/pen hide/ changed, create a new line
        '''
        line1 = my_line(self.window, self._pos_adapter(*self.current_position), self.current_color, self.current_size)

        self.current_line = line1
        self.line_list.append(line1)
        self.last_color = self.current_color
        self.last_pendown_position = self.current_position
        self.last_size = self.current_size

    def _caculate_end(self, direc, distance):
        ret = [0,0]
        d_x = distance * math.cos(self.current_dir * math.pi / 180)
        d_y = distance * math.sin(self.current_dir * math.pi / 180)
        if direc == 1:
            ret[0] = round(self.current_position[0] + d_x)
            ret[1] = round(self.current_position[1] + d_y)
        elif direc == 2:
            ret[0] = round(self.current_position[0] - d_x)
            ret[1] = round(self.current_position[1] - d_y)

        
        return ret

    def _goto(self, end_x, end_y):
        self.current_position = [round(end_x), round(end_y)]

        if self.pen_status == PEN_STA_SHOW:
            self.current_line.add_point(self._pos_adapter(*self.current_position))
        else:
            pass
    
    def _turn_angle(self, angle):
        self.current_dir += angle

        if self.current_dir < 0:
            self.current_dir += 360
        self.current_dir %= 360

    def _move(self, distance):
        self._goto(*self._caculate_end(1, distance))

    def _fill(self):
        pass

    def _circle(self, r, extent=None, step =None):
        self._sync_delay()
        
        if extent == None:
            extent = 360
        if step == None:
            step = 64
            
        move_step_num = round((math.fabs(extent) / 360) * step)
        
        if r < 0 and extent < 0:
            turn = self.left
            move = self.backward
            angle_sign = 1
            move_sign = -1
        elif r < 0 and extent >= 0:
            turn = self.right
            move = self.forward
            angle_sign = -1
            move_sign = 1
        elif r >= 0 and extent < 0:
            turn = self.right
            move = self.backward
            angle_sign = -1
            move_sign = -1
        elif r >= 0 and extent >= 0:
            turn = self.left
            move = self.forward
            angle_sign = 1
            move_sign = 1
            
                   
        step_angle = (360 / step) * angle_sign
        step_distance = (math.fabs(2 * r * math.sin(math.pi / step))) * move_sign
        
        # turn(step_angle / 2)
        # move(step_distance)
        self._turn_angle(step_angle / 2)
        self._move(step_distance)
        for i in range(move_step_num - 1):
            self._turn_angle(step_angle)
            self._move(step_distance)

############################################
    def add_tag(self):
        # use this line as new line
        self._create_new_line()

        self.tag_list.append(len(self.line_list))

        self.tag_id += 1
        
        return self.tag_id

    def clear_tags(self):
        self.tag_list = [0]

        self.tag_id = 0
        
        return self.tag_id

    def tag_move(self, tag_id, x = None, y = None):
        if tag_id > self.tag_id or tag_id <= 0:
            return
        
        start_id = self.tag_list[tag_id - 1]
        # print(start_id, self.tag_list, self.line_list)

        for i in range(self.tag_list[tag_id] - start_id):
            if x != None:
                self.line_list[start_id + i].set_x(self.line_list[start_id + i].get_x() + x)
            if y != None:
                self.line_list[start_id + i].set_y(self.line_list[start_id + i].get_y() - y)

    def hide(self):
        self.window.set_hidden(True)

    def show(self):
        self.window.set_hidden(False)
    
    def set_scale(self, val):
        self.scale_cof = val
########################################################################################

    def forward(self, distance): #fd()
        self._sync_delay()
        
        self._goto(*self._caculate_end(1, distance))

    def backward(self, distance): # bk() | back()
        self._sync_delay()

        self._goto(*self._caculate_end(2, distance))

    def left(self, angle): #lt
        self._sync_delay()

        self._turn_angle(angle)
    
    def right(self, angle): # rt
        self._sync_delay()

        self._turn_angle(-angle)

    
    def circle2(self, r, x = None, y = None, a1 = 0, a2 = 360):
        self._sync_delay()


        # Create style for the Arcs
        style = lv.style_t()
        lv.style_copy(style, lv.style_plain)
        style.line.color = lv.color_make(*self.current_color)
        style.line.width = self.current_size   

        # Create an Arc
        arc = lv.arc(self.window)
        arc.set_style(lv.arc.STYLE.MAIN, style)
        arc.set_angles(a1, a2)
        arc.set_size(r, r)
        arc.align(None, lv.ALIGN.IN_TOP_LEFT, 0, 0)
        if x == None or y == None:
            arc.set_pos(*self._pos_adapter(*self.current_position))
        else:
            arc.set_pos(*self._pos_adapter(x, y))
    
    def goto(self, x, y=None): # | setpos() | setposition() 前往/定位
        self._sync_delay()

        self._goto(x, y)

    def setx(self, x): # 设置x坐标:
        self._sync_delay()
        
        self._goto(x, self.current_position[1])

    def sety(self, y): # 设置y坐标
        self._sync_delay()

        self._goto(self.current_position[0], y)

    def setheading(self, to_angle): # | seth() 设置朝向
        self._sync_delay()

        self.current_dir = to_angle
        self.current_dir %= 360

    def home(self): # 返回原点
        self._sync_delay()

        self._goto(0, 0)

        
    def circle(self, r, extent=None, step =None):
        self._circle(r, extent, step)
 
    def dot(self, size=None, *color): # 画点
        self._sync_delay()
        pass

    def stamp(self):# 印章
        self._sync_delay()

        print("no implement")
        return 0

    def clearstamp(self, stampid):# 清除印章
        self._sync_delay()

        print("no implement")
        return 0

    def clearstamps(self, n=None):# 清除多个印章
        self._sync_delay()

        print("no implement")
        return 0

    def undo(self):# 撤消
        self._sync_delay()

        print("no implement")
        return 0

    def speed(self, speed=None): #速度
        print("no implement")
        return 0

#############################################
    def position(self): # | pos() 位置
        return  self.current_position

    def towards(self, x, y=None): # 目标方向
        self._sync_delay()

        self.current_dir = math.atan2(x - self.current_position[0], y - self.current_position[1])
        if self.current_dir < 0:
            self.current_dir += 360
        self.current_dir %= 360

    def xcor(self):# x坐标
        self.current_position[0]

    def ycor(self):# y坐标
        return self.current_position[1]

    def heading(self): #朝向
        return self.current_dir

    def distance(self, x, y=None):# 距离
        return math.sqrt(math.pow(self.current_position[0] - x, 2), math.pow(self.current_position[1] - y, 2)) 

    def degrees(self, fullcircle=360.0): #角度
        self.angle_unit = ANGLE_UNIT_DGREE

    def radians(self): #弧度
        self.angle_unit = ANGLE_UNIT_RADIANS

########################################################
    def pendown(self):# | pd() | down() 画笔落下
        self._sync_delay()

        if self.pen_status == PEN_STA_HIDE:
            self._create_new_line()
            self.pen_status = PEN_STA_SHOW

    def penup(self): #| pu() | up() 画笔抬起
        self._sync_delay()

        self.pen_status = PEN_STA_HIDE
    
    def pensize(self, size):
        self._sync_delay()

        if self.pen_status == PEN_STA_SHOW and self.last_size != size:
            self._create_new_line()
            self.current_line.style_line.line.width = size
        
        self.current_size = size

    def pencolor(self, r, g = 0, b = 0):
        self._sync_delay()

        if self.pen_status == PEN_STA_SHOW and self.last_color != rgb_limit(r, g, b):
            self._create_new_line()
            self.current_line.style_line.line.color = lv.color_make(*rgb_limit(r, g, b))
        self.current_color = rgb_limit(r, g, b)

    def pen(self): 
        print("no implement")
        return 0

    def isdown(self):
        if self.pen_status == PEN_STA_SHOW:
            return True
        else:
            return False
    
    def color(self, r, g = 0, b = 0):
        self.pencolor(r, g, b)
    
    def fillcolor(self):
        print("no implement")
        return 0
    
    def filling(self):
        return self.filling_status
    
    def begin_fill(self):
        self.filling_status = True
    
    def end_fill(self):
        self._fill()

        self.filling_status = False
        self.filling_points = []
    
    def reset(self):
        pass 
    
    def write(self, txt):
        label = my_label(self.window)
        if self.line_list[-1].type == "line":
            label.set_style(lv.label.STYLE.MAIN, self.line_list[-1].get_style(lv.line.STYLE.MAIN))
        else:
            label.set_style(lv.label.STYLE.MAIN, self.line_list[-1].get_style(lv.label.STYLE.MAIN))

        label.set_text(str(txt))
        label.set_pos(*self._pos_adapter(*self.current_position))

        self.line_list.append(label)

########################################################
    def bgcolor(self, r, g = 0, b = 0):
        self._sync_delay()

        self.window_style.body.main_color = lv.color_make(*rgb_limit(r, g, b))
        self.window.refresh_style()

    def bgpic(self):
        pass

    def clear(self): # | clearscreen()
        self._sync_delay()
        self.window.clean()
        self._reset(self._pos_adapter(*self.current_position), self.current_color)
        self._goto(0, 0)

    
    def reset(self):# | resetscreen() 重置
        pass
    
    def screensize(self):# 屏幕大小
        pass
    
    def setworldcoordinates(self):# 设置世界坐标系
        pass
    
    def tracer(self, sta):
        self.tracer_flag = sta
#############################################
    def showturtle(self): # | st()
        pass

    def hideturtle(self):# | ht() 
        pass

    def isvisible(self):#
        pass 

    def shape(self):#
        pass 

    def resizemode(self):#
        pass 

    def shapesize(self):# | turtlesize()
        pass 

    def shearfactor(self):#
        pass 

    def settiltangle(self):#
        pass 

    def tiltangle(self):# 
        pass

    def tilt(self):#
        pass 

    def shapetransform(self):# 
        pass

    def get_shapepoly(self):#
        pass 


import display.display_user_desktop  as user_desktop

# window = user_desktop.get_app().get_user_window()
# window.set_size(128, 128)
# _turtle = pen(window)

# def _not_implement():
#     print("not implement")

# add_tag = _turtle.clear
# clear_tags = _turtle.clear_tags
# tag_move = _turtle.tag_move
# hide = _turtle.hide
# show = _turtle.show
# clear = _turtle.clear
# set_scale = _turtle.set_scale
# forward = _turtle.forward
# backward = _turtle.backward
# left = _turtle.left
# right = _turtle.right

# goto = _turtle.goto
# setx = _turtle.setx
# sety  = _turtle.sety
# setheading  = _turtle.setheading
# home  = _turtle.home
# circle  = _turtle.circle
# dot  = _not_implement
# stamp  = _not_implement
# clearstamp  = _not_implement
# clearstamps  = _not_implement
# undo  = _not_implement
# speed  = _not_implement

# towards  = _not_implement
# position = _turtle.position
# xcor  = _turtle.xcor
# ycor  = _turtle.ycor
# heading  = _turtle.heading
# distance  = _turtle.distance
# degrees = _not_implement
# radians = _not_implement
# pendown  = _turtle.pendown
# penup  = _turtle.penup
# pensize  = _turtle.pensize
# pencolor  = _turtle.pencolor
# isdown  = _turtle.isdown
# color  = _turtle.color
# fillcolor  = _not_implement
# filling  = _not_implement
# begin_fill  = _not_implement
# end_fill  =_not_implement
# write  = _turtle.write
# bgcolor  = _turtle.bgcolor
# bgpic  =_not_implement

# fd = forward
# bk = backward
# back = backward
# lt = left
# rt = right
# setpos = goto
# setposition = goto
# seth = setheading
# up = penup 
# pu = penup 
# down = pendown
# pd = pendown
# pos = position